#include "FileDownloadRequestSenderProcess.h"

int FileDownloadRequestSenderProcess::downloadsCounter = 0;

FileDownloadRequestSenderProcess::FileDownloadRequestSenderProcess(LogFile* logFile) : Process(logFile) {
}

FileDownloadRequestSenderProcess::~FileDownloadRequestSenderProcess() {
}

void FileDownloadRequestSenderProcess::setDownloadDirectory(string downloadDirectory) {
	mkdir(downloadDirectory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

	this->downloadDirectory = downloadDirectory;
}

void FileDownloadRequestSenderProcess::setFiles(list<string> files) {
	this->files = files;
}

string FileDownloadRequestSenderProcess::getProcessName() {
	return string("FileDownloadRequestSender");
}

bool FileDownloadRequestSenderProcess::sendRequest(string fifoReceptor, SharedRecord requestToSend) {
	RequestFifo requestFifo(fifoReceptor, this->logFile);

	return requestFifo.writeRequest(requestToSend);
}

string FileDownloadRequestSenderProcess::getUniqueDownloadFifoName() {
	FileDownloadRequestSenderProcess::downloadsCounter++;

	stringstream stream;
	stream << ROOTDIRECTORY << DOWNLOADFIFOBASENAME << getpid();
	stream << "-D" << FileDownloadRequestSenderProcess::downloadsCounter;

	return stream.str();
}

int FileDownloadRequestSenderProcess::doWork() {
	SharedFile file;
	SharedRecord request;
	list<SharedRecord> requests;
	list<string>::iterator filesIterator;

	// Tomar un lock del archivo compartido para lectura.
	logFile->log("[" + this->getProcessName() + "] Bloqueando archivo compartido para lectura.");
	file.getReadLock();

	// Validar archivos a decargar.
	logFile->log("[" + this->getProcessName() + "] Recorriendo archivo compartido para validar los archivos a descargar.");
	file.goTo(0);
	while (!file.isEOF() && (this->files.size() != 0)) {
		request = file.getNextRecord();
		string filePath = request.sharedFilePath;

		filesIterator = find(this->files.begin(), this->files.end(), filePath);
		if ((filesIterator != this->files.end()) && (*filesIterator == filePath)) {
			requests.push_back(request);
			this->files.erase(filesIterator);

			logFile->log("[" + this->getProcessName() + "] Request de descarga armado (file: " + request.sharedFilePath + " - pipe: " + request.requestFifo);
		}
	}

	filesIterator = this->files.begin();
	while (filesIterator != this->files.end()) {
		logFile->log("[" + this->getProcessName() + "] El archivo " + *filesIterator + " sera ignorado ya que no esta siendo compartido.");
		filesIterator++;
	}

	// Armo request de descarga, lo mando y excluyo los archivos que tuvieron errores.
	list<SharedRecord>::iterator requestsIterator = requests.begin();
	string requestPipe = "";
	while(requestsIterator != requests.end()) {
		requestPipe = requestsIterator->requestFifo;

		strcpy(requestsIterator->requestFifo, FileDownloadRequestSenderProcess::getUniqueDownloadFifoName().c_str());

		if (!this->sendRequest(requestPipe, *requestsIterator)) {
			logFile->log("[" + this->getProcessName() + "] Hubo un error al mandar el request de descarga (file: " + request.sharedFilePath + " - pipe: " + request.requestFifo + ") por el pipe " + requestPipe);
			requestsIterator = requests.erase(requestsIterator);
		}
		else {
			requestsIterator++;
		}
	}

	// Liberar el lock del archivo compartido.
	logFile->log("[" + this->getProcessName() + "] Desbloqueando archivo compartido.");
	file.unlock();

	// Creo una instancia del proceso de descarga de archivos.
	FileDownloaderProcess fileDownloaderProcess(this->logFile);
	fileDownloaderProcess.setParentProcessName(this->getProcessName());

	// Disparar un proceso de descarga para cada archivo.
	requestsIterator = requests.begin();
	while (requestsIterator != requests.end()) {
		string requestedFile = requestsIterator->sharedFilePath;
		string requestFifoPath = requestsIterator->requestFifo;

		int lastBar = requestedFile.rfind('/');
		string downloadFilePath = this->downloadDirectory + requestedFile.substr(lastBar);

		this->logFile->log("[" + this->getProcessName() + "] " + "Iniciando proceso de descarga del archivo " + requestedFile + " por el fifo " + requestFifoPath + " guardandolo en " + downloadFilePath);
		fileDownloaderProcess.setDownloadFifoPath(requestFifoPath);
		fileDownloaderProcess.setDownloadFilePath(downloadFilePath);
		fileDownloaderProcess.start();

		++requestsIterator;
	}

	// Espero las descargas pendientes.
	this->logFile->log("[" + this->getProcessName() + "] Esperando que finalizen las descargas pendientes.");
	fileDownloaderProcess.wait();

	return 0;
}
